﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using Azure.Logger;

namespace Azure.Configuration
{
    public class AzConfigManager
    {
        private static XElement s_azureConfig;
        //Key = Robot name
        //Value = AzRobotConfigItem
        private static Dictionary<string, AzRobotConfigItem> s_robotDict = new Dictionary<string,AzRobotConfigItem>();

        //如果机器人没有名字，则使用默认名字: DefaultRobot - index
        private const String DEFAULT_ROBOT_NAME = "DefaultRobot - ";
        
        private static int s_defaultRobotIndex = 0;

        public static Dictionary<string, AzRobotConfigItem> AllRobotConfig { get { return s_robotDict; } }

        public static void Init()
        {
            AzLogger.Info("开始初始化YourAzure配置文件...");
            try
            {
                s_azureConfig = XElement.Load("azure.config");
            }
            catch (Exception e)
            {
                AzLogger.Error("加载azure.config时发生错误", e);
                Environment.Exit(0);
            }

            //验证azure.config的格式是否正确,如果错误，程序自动结束
            AzConfigValidator.Validate(s_azureConfig.CreateReader());

            //开始使用LINQ填充各个配置项数据结构
            if (!BuildRobots())
            {
                AzLogger.Important("配置文件中不包含合规的Robot，退出运行");
                Environment.Exit(0);
            }

            AzLogger.Info("初始化YourAzure的配置文件成功！");
        }

        private static bool BuildRobots()
        {
            //从最顶层的Robot开始
            IEnumerable<XElement> robotElementList = s_azureConfig.Elements("Robot");
            bool atLeastOneValidRobot = false;

            foreach (XElement robotElement in robotElementList)
            {
                AzRobotConfigItem robotItem = new AzRobotConfigItem();

                XAttribute nameAttr = robotElement.Attribute("name");

                #region 名字属性，如果没有就是用默认名字
                if (nameAttr == null)
                {
                    String defName = String.Empty;

                    //检查字典中是否已经有这个名字存在了，有则使用下一个索引，防止和用户定义的名字冲突，造成覆盖
                    do
                    {
                        defName = DEFAULT_ROBOT_NAME + s_defaultRobotIndex;
                        s_defaultRobotIndex++;
                    } while (s_robotDict.ContainsKey(defName));

                    robotItem.Name = defName;
                }
                else
                {
                    robotItem.Name = nameAttr.Value;
                }

                //检查是否有同名的机器人
                if (s_robotDict.Keys.Contains(robotItem.Name))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "重名了，跳过当前机器人");
                    continue;
                }
                #endregion

                //获取Parser的配置信息
                if (!BuildParsersForRobot(robotElement, robotItem))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "没有对应的Parser或者所有Parser都不合规");
                    continue;
                }

                //获取Loader的配置信息
                if (!BuildLoaderForRobot(robotElement, robotItem))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "没有对应的Loader或者所有Loader都不合规");
                    continue;
                }

                //获取Serializer的配置信息
                if (!BuildSerializerForRobot(robotElement, robotItem))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "没有对应的Serializer或者所有Serializer都不合规");
                    continue;
                }

                //获取Robot的剩余属性
                #region 获取startCrawlingUri
                String startUriAttr = (String)robotElement.Attribute("startCrawlingUri");

                if (startUriAttr == String.Empty)
                {
                    //如果没有起始URI，则跳过该机器人。
                    AzLogger.Warn("机器人" + robotItem.Name + "的startCrawlingUri属性内容为空");
                    continue;
                }
                else
                {
                    robotItem.StartCrawlingUri = startUriAttr;
                }
                #endregion

                #region 获取startPaserName, 他必须是Robot包含的某个parser
                String startParserNameAttr = (String)robotElement.Attribute("startParserName");

                if (startParserNameAttr == String.Empty)
                {
                    //如果没有startParserName，则跳过该机器人。
                    AzLogger.Warn("机器人" + robotItem.Name + "的startParserName属性内容为空");
                    continue;
                }

                //检查是否存在这个parser，如果不存在也跳过
                Dictionary<String, AzParserConfigItem> parsersOfRobot = robotItem.ParserDict;

                if (!parsersOfRobot.Keys.Contains(startParserNameAttr))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "的startParserName属性的内容没有对应的Parser");
                    continue;
                }

                robotItem.StartParserName = startParserNameAttr;
                #endregion

                //至此，一个Robot的配置获得完成
                s_robotDict.Add(robotItem.Name, robotItem);
                atLeastOneValidRobot = true;
            }//end foreach (XElement robotElement in robotElementList)

            return atLeastOneValidRobot;
        }

        private static bool BuildLoaderForRobot(XElement robotElement, AzRobotConfigItem robotItem)
        {
            bool ret = false;

            XElement loaderElem = robotElement.Element("Loader");
            AzLoaderConfigItem loaderItem = new AzLoaderConfigItem();
            //由XSD来确保一定是整型
            loaderItem.MaxRetryCount = int.Parse(loaderElem.Attribute("maxRetryCount").Value);
            loaderItem.TimeOutDelay = int.Parse(loaderElem.Attribute("timeOutDelay").Value);

            robotItem.Loader = loaderItem;
            ret = true;

            return ret;
        }

        private static bool BuildSerializerForRobot(XElement robotElement, AzRobotConfigItem robotItem)
        {
            bool atLeastOneValidSerializer = false;

            IEnumerable<XElement> serializerList = robotElement.Elements("Serializer");

            foreach (XElement serializerElem in serializerList)
            {
                AzSerializerConfigItem serializerItem = new AzSerializerConfigItem();
                serializerItem.Name = serializerElem.Attribute("name").Value;
                serializerItem.AssemblyName = serializerElem.Attribute("assemblyName").Value;
                serializerItem.FullClassName = serializerElem.Attribute("fullClassName").Value;

                //构造函数参数
                IEnumerable<XElement> ctorList = serializerElem.Elements("ctorArg");
                foreach (XElement ctorArgElem in ctorList)
                {
                    serializerItem.CtorArgDict.Add(ctorArgElem.Attribute("name").Value, ctorArgElem.Value);
                }

                //属性参数
                IEnumerable<XElement> propertyList = serializerElem.Elements("property");
                foreach (XElement propertyElem in propertyList)
                {
                    serializerItem.PropertyDict.Add(propertyElem.Attribute("name").Value, propertyElem.Value);
                }

                robotItem.SerializeList.Add(serializerItem);

                atLeastOneValidSerializer = true;
            }

            return atLeastOneValidSerializer;
        }

        private static bool BuildParsersForRobot(XElement robotElement, AzRobotConfigItem robotItem)
        {
            //确保这个机器人中至少有一个完成的parser
            bool atLeastOneValidParser = false;
            //该机器人的parser字典，每成功获得一个parser后就往字典中加入
            Dictionary<String, AzParserConfigItem> parserDict = robotItem.ParserDict;

            IEnumerable<XElement> parserElementList = robotElement.Elements("Parser");

            foreach (XElement parserElem in parserElementList)
            {
                string name = parserElem.Attribute("name").Value;
                AzParserConfigItem parserItem = null;

                if (name == String.Empty)
                {
                    //如果名字为空，就跳过这个parser
                    AzLogger.Warn("机器人" + robotItem.Name + "的某个Parser名字为空");
                    continue;
                }

                //这个Parser的名字已经在字典中存在了，说明这个机器人中有重名的parser，跳过当前的
                if (parserDict.Keys.Contains(name))
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "拥有重名的Parser = " + name + "。 跳过该parser");
                    continue;
                }
                else
                {
                    parserItem = new AzParserConfigItem();
                    parserItem.Name = name;
                }

                //获取所有UriTarget
                bool atLeastOneValidUriTarget = BuildUriTargetsForParser(parserElem, parserItem);
                //获取所有BlockTarget
                bool atLeastOneValidBlockTarget = BuildBlockTargetsForParser(parserElem, parserItem);

                if (!atLeastOneValidBlockTarget && !atLeastOneValidUriTarget)
                {
                    AzLogger.Warn("机器人" + robotItem.Name + "的Parser " + parserItem.Name + "没有合规的Target");
                    continue;
                }

                parserDict.Add(parserItem.Name, parserItem);
                atLeastOneValidParser = true;
            }//end foreach (XElement parserElem in parserElementList)

            return atLeastOneValidParser;
        }

        private static bool BuildUriTargetsForParser(XElement parserElem, AzParserConfigItem parserItem)
        {
            IEnumerable<XElement> uriTargetList = parserElem.Elements("UriTarget");
            bool atLeastOneValidUriTarget = false;
            Dictionary<String, AzUriTargetConfigItem> uriTargetDict = parserItem.UriTargetDict;

            foreach (XElement uriTargetElem in uriTargetList)
            {
                string name = (String)uriTargetElem.Attribute("name");
                AzUriTargetConfigItem uriTargetItem = null;

                if (name == String.Empty)
                {
                    AzLogger.Warn("分析器" + parserItem.Name + "的某个UriTarget名字为空");
                    continue;
                }

                if (uriTargetDict.Keys.Contains(name))
                {
                    AzLogger.Warn("分析器" + parserItem.Name + "拥有重名的UriTarget");
                    continue;
                }
                else
                {
                    uriTargetItem = new AzUriTargetConfigItem();
                    uriTargetItem.Name = name;
                }

                //获取其他属性
                uriTargetItem.ParserName = (String)uriTargetElem.Attribute("parserName");

                //获取Formula子元素
                XElement formulaElem = uriTargetElem.Element("Formula");
                AzFormulaConfigItem formulaItem = new AzFormulaConfigItem();
                formulaItem.Regex = (String)formulaElem.Element("Regex").Value;
                formulaItem.UniqueMatch = Boolean.Parse(formulaElem.Attribute("uniqueMatch").Value);
                XElement prefixNode = formulaElem.Element("Prefix");
                
                if (prefixNode != null)
                    formulaItem.Prefix = prefixNode.Value;
                else
                    formulaItem.Prefix = String.Empty;

                //获得Formula的GroupName子元素
                IEnumerable<XElement> groupNameList = formulaElem.Elements("GroupName");
                foreach (XElement groupName in groupNameList)
                {
                    formulaItem.GroupNameList.Add(groupName.Value);
                }

                uriTargetItem.Formula = formulaItem;

                //获取ErrorHandling子元素
                IEnumerable<XElement> errorHandlingList = uriTargetElem.Elements("ErrorHandling");

                foreach (XElement errorHandlingElem in errorHandlingList)
                {
                    AzErrorHandlingConfigItem errorHandlingItem = new AzErrorHandlingConfigItem();

                    //获取operation属性
                    errorHandlingItem.Operation = (String)errorHandlingElem.Attribute("operation");

                    //获取param属性，这个属性是针对operation而言的，不同的operation，该属性的值的作用不同
                    errorHandlingItem.Param = (String)errorHandlingElem.Attribute("param");

                    uriTargetItem.ErrorHandlingList.Add(errorHandlingItem);
                }

                //加到字典中
                uriTargetDict.Add(uriTargetItem.Name, uriTargetItem);

                atLeastOneValidUriTarget = true;
            }//end foreach (XElement uriTargetElem in uriTargetList)

            return atLeastOneValidUriTarget;
        }

        private static bool BuildBlockTargetsForParser(XElement parserElem, AzParserConfigItem parserItem)
        {
            IEnumerable<XElement> blockTargetList = parserElem.Elements("BlockTarget");
            bool atLeastOneValidBlockTarget = false;
            Dictionary<String, AzBlockTargetConfigItem> blockTargetDict = parserItem.BlockTargetDict;

            foreach (XElement blockTargetElem in blockTargetList)
            {
                String name = (String)blockTargetElem.Attribute("name");
                AzBlockTargetConfigItem blockTargetItem = null;

                if (name == String.Empty)
                {
                    AzLogger.Warn("分析器" + parserItem.Name + "的某个BlockTarget名字为空");
                    continue;
                }

                if (blockTargetDict.Keys.Contains(name))
                {
                    AzLogger.Warn("分析器" + parserItem.Name + "拥有重名的BlockTarget");
                    continue;
                }
                else
                {
                    blockTargetItem = new AzBlockTargetConfigItem();
                    blockTargetItem.Name = name;
                }

                //对BlockTarget中的每个Target进行获取
                IEnumerable<XElement> targetList = blockTargetElem.Elements("Target");
                Dictionary<String, AzTargetConfigItem> targetDict = blockTargetItem.TargetDict;

                foreach (XElement targetElem in targetList)
                {
                    String targetName = (String)targetElem.Attribute("name");
                    AzTargetConfigItem targetItem = null;

                    if (targetName == String.Empty)
                    {
                        AzLogger.Warn("块目标" + blockTargetItem.Name + "的某个Target名字为空");
                        continue;
                    }

                    if (targetDict.Keys.Contains(targetName))
                    {
                        AzLogger.Warn("块目标" + blockTargetItem.Name + "拥有重名的Target");
                        continue;
                    }
                    else
                    {
                        targetItem = new AzTargetConfigItem();
                        targetItem.Name = targetName;
                    }

                    //获取Formula子元素
                    XElement formulaElem = targetElem.Element("Formula");
                    AzFormulaConfigItem formulaItem = new AzFormulaConfigItem();
                    formulaItem.Regex = (String)formulaElem.Element("Regex").Value;

                    //获得Formula的GroupName子元素
                    IEnumerable<XElement> groupNameList = formulaElem.Elements("GroupName");
                    foreach (XElement groupName in groupNameList)
                    {
                        formulaItem.GroupNameList.Add(groupName.Value);
                    }

                    targetItem.Formula = formulaItem;

                    //获取属性
                    targetItem.Type = (String)targetElem.Attribute("type");
                    targetItem.DefaultValue = (String)targetElem.Attribute("defaultValue");

                    targetDict.Add(targetItem.Name, targetItem);
                }//end foreach (XElement targetElem in targetList)

                blockTargetDict.Add(blockTargetItem.Name, blockTargetItem);

                atLeastOneValidBlockTarget = true;
            }//end foreach (XElement blockTargetElem in blockTargetList)

            return atLeastOneValidBlockTarget;
        }
    }//end class AzConfigManager

    public class AzRobotConfigItem
    {
        public AzLoaderConfigItem Loader { get; set; }
        public Dictionary<string, AzParserConfigItem> ParserDict { get; set; }
        public List<AzSerializerConfigItem> SerializeList { get; set; }
        public String Name { get; set; }
        public String StartCrawlingUri { get; set; }
        public String StartParserName { get; set; }

        public AzRobotConfigItem()
        {
            ParserDict = new Dictionary<string, AzParserConfigItem>();
            SerializeList = new List<AzSerializerConfigItem>();
        }
    }

    public class AzLoaderConfigItem
    {
        public int MaxRetryCount { get; set; }
        public int TimeOutDelay { get; set; }
    }

    public class AzParserConfigItem
    {
        public Dictionary<String, AzBlockTargetConfigItem> BlockTargetDict { get; set; }
        public Dictionary<String, AzUriTargetConfigItem> UriTargetDict { get; set; }
        public String Name { get; set; }

        public AzParserConfigItem()
        {
            BlockTargetDict = new Dictionary<string, AzBlockTargetConfigItem>();
            UriTargetDict = new Dictionary<string, AzUriTargetConfigItem>();
        }
    }

    public class AzUriTargetConfigItem
    {
        public List<AzErrorHandlingConfigItem> ErrorHandlingList { get; set; }
        public AzFormulaConfigItem Formula { get; set; }
        public String Name { get; set; }
        public String ParserName { get; set; }

        public AzUriTargetConfigItem()
        {
            ErrorHandlingList = new List<AzErrorHandlingConfigItem>();
        }
    }

    public class AzErrorHandlingConfigItem
    {
        public String Operation { get; set; }
        public String Param { get; set; }
    }

    public class AzBlockTargetConfigItem
    {
        public Dictionary<String, AzTargetConfigItem> TargetDict { get; set; }
        public String Name { get; set; }

        public AzBlockTargetConfigItem()
        {
            TargetDict = new Dictionary<string, AzTargetConfigItem>();
        }
    }

    public class AzTargetConfigItem
    {
        public String Name { get; set; }
        public AzFormulaConfigItem Formula { get; set; }
        public String Type { get; set; }
        public String DefaultValue { get; set; }
    }

    public class AzFormulaConfigItem
    {
        public String Regex { get; set; }
        public String Prefix { get; set; }
        public bool UniqueMatch { get; set; }
        public List<String> GroupNameList { get; set; }

        public AzFormulaConfigItem()
        {
            GroupNameList = new List<String>();
        }
    }

    public class AzSerializerConfigItem
    {
        public String Name { get; set; }
        public String AssemblyName { get; set; }
        public String FullClassName { get; set; }

        //key = name, value = value
        public Dictionary<String, String> CtorArgDict { get; set; }
        //key = name, value = value
        public Dictionary<String, String> PropertyDict { get; set; }

        public AzSerializerConfigItem()
        {
            CtorArgDict = new Dictionary<string, string>();
            PropertyDict = new Dictionary<string, string>();
        }
    }
}
